#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <cstdio>

#include "Protocol.hpp"
#include "Socket.hpp"

//const static int default_sockfd = -1;
const static int max_reconnect_count = 5;
const static int reconnect_time = 1;

// 描述TCP套接字的状态
enum class Status
{
    NEW,          // 新建
    CONNECTING,   // 连接中
    CONNECTED,    // 已连接
    DISCONNECTED, // 断链
    CLOSED        // 重连失败，关闭
};

enum Code
{
    SOCK_ERROR = 1,
};

class TcpClientConnection
{
public:
    TcpClientConnection(InetAddr serverAddr, mySocket::socket_ptr sockfd)
        : _serverAddr(serverAddr),
          _sockfd(sockfd),
          _reconnect_time(reconnect_time),
          _max_reconnect_count(max_reconnect_count),
          _status(Status::NEW)
    {
    }

    void Connect()
    {
        _sockfd->socketCreate();

        _status = Status::NEW;

        int n = _sockfd->socketConnect(_serverAddr);
        if (n < 0)
        {
            _status = Status::DISCONNECTED;
            _sockfd->socketClose();
            return;
        }

        _status = Status::CONNECTED;
    }

    void ReConnect()
    {
        _status = Status::CONNECTING;
        std::cout << "重连中......" << std::endl;

        int cnt = 0;
        while (true)
        {
            printf("第%d重连......\n", cnt++);
            Connect();

            if (_status == Status::CONNECTED)
            {
                std::cout << "重连成功......" << std::endl;
                break;
            }
            if (cnt == _max_reconnect_count)
            {
                std::cout << "重连失败，请检查你的网络" << std::endl;
                break;
            }

            sleep(_reconnect_time);
        }
    }

    void Process()
    {
        srand((unsigned)time(nullptr));

        char option[] = {"+-*/%^|"};
        for (auto c : option)
            printf("%c:%d ", c, c);
        std::cout << std::endl;

        std::string server_buffer;
        while (true)
        {
            std::cout << "-----------------begin-----------------" << std::endl;
            int x = rand() % 100 + 1;
            int y = rand() % 5;
            char op = option[rand() % 7];

            Request request(x, y, op);
            // 序列化
            std::string client_massge;
            request.Serialize(&client_massge);

            std::cout << "client send: " << std::endl
                      << client_massge << std::endl;

            // 添加报头
            Encode(client_massge);

            // 发送
            int n = _sockfd->socketSend(client_massge);
            if (n < 0)
            {
                std::cout << "send() error" << std::endl;
                _status = Status::DISCONNECTED;
                return;
            }

            // 接收
            std::string package;
            // 判断是否收到完整的信息
            while (true)
            {
                int n = _sockfd->socketRecv(&server_buffer);
                if (n <= 0)
                {
                    std::cout << "recv() error" << std::endl;
                    _status = Status::DISCONNECTED;
                    return;
                }
                //std::cout << "server_inbuffer: " << server_buffer << std::endl;
                package = DeCode(server_buffer);
                //std::cout << "package: " << package << std::endl;

                if (package.empty())
                    continue;

                break;
            }

            // 收到了完整信息
            // 反序列化
            Response response;
            response.Deserialize(package);

            std::cout << "Server response: ";
            printf("Server response: code: %d, result: %d\n", response.getCode(), response.getResult());

            sleep(2);
        }
    }

    void CloseConnection()
    {
        _sockfd->socketClose();
        _status = Status::CLOSED;
    }

    Status ClientStatus()
    {
        return _status;
    }

private:
    Status _status;
    mySocket::socket_ptr _sockfd;
    InetAddr _serverAddr;
    int _max_reconnect_count; // 最大重连次数
    int _reconnect_time;      // 重连时间间隔

};

class TcpClient
{
public:
    TcpClient(mySocket::socket_ptr sockfd, InetAddr serverAddr)
        : _client(TcpClientConnection(serverAddr, sockfd))
    {
    }

    void Execute()
    {
        while (true)
        {
            switch (_client.ClientStatus())
            {
            case Status::NEW:
                _client.Connect();
                break;
            case Status::CONNECTED:
                _client.Process();
                break;
            case Status::DISCONNECTED:
                _client.ReConnect();
                break;
            case Status::CLOSED:
                _client.CloseConnection();
                break;
            default:
                break;
            }
        }
    }

private:
    TcpClientConnection _client;
};

void Usage(char *argv[])
{
    printf("%s\n\t[IP] [Port]", argv[0]);
}

int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv);
        exit(1);
    }

    std::string serverIp = argv[1];
    uint16_t serverPort = std::stoi(argv[2]);

    mySocket::tcpSocket clientSock;
    
    TcpClient client(&clientSock, InetAddr(serverPort, serverIp));

    client.Execute();

    return 0;
}